package tuvienna.jade;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.core.behaviours.WakerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.lang.acl.UnreadableException;
import jade.util.Logger;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 *
 */
public class EvaluatorAgent extends Agent{

	private static final long serialVersionUID = -830954558045520583L;

	private static Logger myLogger = Logger.getMyLogger("EvaluatorAgent");

	Map<AID,ParticipantStatus> status = new HashMap<AID,ParticipantStatus>();
	
	Map<AID,Set<AID>> tables = new TreeMap<AID,Set<AID>>();
	private int evaluationIntervall = 5000;
	
	protected void setup() {
		
		Object[] args = getArguments();
		if (args != null) { 
			int cur = -1;
			while(++cur < args.length) {
				if("-evaluationIntervall".equals(args[cur]) &&
				   ++cur < args.length)
					try	{	
						this.evaluationIntervall = Integer.parseInt(args[cur].toString());
					} catch(NumberFormatException e)	{
						System.out.println("Parsing evaluationIntervall failed. \n " +e.getMessage());
					}
			}
		}
		try { //register service
			DFAgentDescription dfd = new DFAgentDescription();
			dfd.setName(getAID());
			ServiceDescription sd = new ServiceDescription();
			sd.setType(Constants.EVALUATOR_REGISTRATION_SERVICE);
			sd.setName(getName());
			sd.setOwnership("TU Vienna");
			dfd.addServices(sd);
			
			DFService.register(this, dfd);
		}
		catch (FIPAException fe) {
			fe.printStackTrace();
			myLogger.log(Logger.SEVERE, "Agent "+getLocalName()+" - Cannot register with DF", fe);
			doDelete();
			return;
		}
		
		addBehaviour(new WakerBehaviour(this, 5000) {
			private static final long serialVersionUID = 5065435394997973985L;
			protected void onWake() {
				addBehaviour(new TickerBehaviour(EvaluatorAgent.this, evaluationIntervall) {
					private static final long serialVersionUID = -7538746921977978572L;
					protected void onTick() {
						addBehaviour(new ComputingClusteringCoefficient());
					}
				});
			}
		});
		
		addBehaviour(new StatusChangedBehavior());
	}
	
	private class ComputingClusteringCoefficient extends OneShotBehaviour {
		private static final long serialVersionUID = -1974042780223679991L;

		public void action() {
			
			double ccoeff = ParticipantStatus.computeClusteringCoefficient(EvaluatorAgent.this.status.values());
			System.out.print(printTableStructure());
			System.out.println("Clustering Coefficient: " + round(ccoeff));
			
			}
			
		private double round(double d) {
			return ((int)(d*1000))/1000.0;
		}
	}
			
	private String printTableStructure() {
		StringBuffer buffer = new StringBuffer();
		
		for (AID table: tables.keySet()) {
			buffer.append(table.getLocalName()+"{");
			for (AID participant: tables.get(table)) {
				buffer.append(participant.getLocalName()+",");
			}
			buffer.append("}\r\n");
		}
		return buffer.toString();
	}
	
	class StatusChangedBehavior extends CyclicBehaviour {
		private static final long serialVersionUID = 1641623684280147404L;
	
		@Override
		public void action() {
			ACLMessage msg = EvaluatorAgent.this.receive(MessageTemplate.MatchConversationId(Constants.EVALUATION));
			if(msg!=null)
				try {
					ParticipantStatus msgStatus = (ParticipantStatus) msg.getContentObject();
					AID participant =  msg.getSender();

					status.put(participant, msgStatus);
					
					putParticipantOnTable(msgStatus.getTable(), participant);
				} catch (UnreadableException e) {
						e.printStackTrace();
				}
			else
				block();
		}	
	} 

	void putParticipantOnTable(AID table, AID participant) {

		// delete from other table - were it came from
		for (Set<AID> otherTableSet: tables.values()) {
			otherTableSet.remove(participant);
		}
		
		if (table != null) {
			Set<AID> tableSet = tables.get(table);
			// table doesn't exist
			if (tableSet == null) {
				tableSet = new TreeSet<AID>();
				tables.put(table, tableSet);
			}
			
			tableSet.add(participant);
		}		
	}
}
